﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace IntoCommon.Protocol.ZLGCAN
{
    public class CANCommunicator
    {
        #region struct
        const int NULL = 0;
        const int CANFD_BRS = 0x01; /* bit rate switch (second bitrate for payload data) */
        const int CANFD_ESI = 0x02; /* error state indicator of the transmitting node */

        /* CAN payload length and DLC definitions according to ISO 11898-1 */
        const int CAN_MAX_DLC = 8;
        const int CAN_MAX_DLEN = 8;

        /* CAN FD payload length and DLC definitions according to ISO 11898-7 */
        const int CANFD_MAX_DLC = 15;
        const int CANFD_MAX_DLEN = 64;

        const uint CAN_EFF_FLAG = 0x80000000U; /* EFF/SFF is set in the MSB */
        const uint CAN_RTR_FLAG = 0x40000000U; /* remote transmission request */
        const uint CAN_ERR_FLAG = 0x20000000U; /* error message frame */
        const uint CAN_ID_FLAG = 0x1FFFFFFFU; /* id */

        DeviceInfo[] kDeviceType =
        {
            new DeviceInfo(Define.ZCAN_USBCAN1, 1),
            new DeviceInfo(Define.ZCAN_USBCAN2, 2),
            new DeviceInfo(Define.ZCAN_USBCAN_E_U, 1),
            new DeviceInfo(Define.ZCAN_USBCAN_2E_U, 2),
            new DeviceInfo(Define.ZCAN_PCIECANFD_100U, 1),
            new DeviceInfo(Define.ZCAN_PCIECANFD_200U, 2),
            new DeviceInfo(Define.ZCAN_PCIECANFD_200U_EX,2),
            new DeviceInfo(Define.ZCAN_PCIECANFD_400U, 4),
            new DeviceInfo(Define.ZCAN_USBCANFD_200U, 2),
            new DeviceInfo(Define.ZCAN_USBCANFD_100U, 1),
            new DeviceInfo(Define.ZCAN_USBCANFD_MINI, 1),
            new DeviceInfo(Define.ZCAN_CANETTCP, 1),
            new DeviceInfo(Define.ZCAN_CANETUDP, 1),
            new DeviceInfo(Define.ZCAN_CANFDNET_200U_TCP, 2),
            new DeviceInfo(Define.ZCAN_CANFDNET_200U_UDP, 2),
            new DeviceInfo(Define.ZCAN_CANFDNET_400U_TCP, 4),
            new DeviceInfo(Define.ZCAN_CANFDNET_400U_UDP, 4),
            new DeviceInfo(Define.ZCAN_CANFDNET_800U_TCP, 8),
            new DeviceInfo(Define.ZCAN_CANFDNET_800U_UDP, 8),
            new DeviceInfo(Define.ZCAN_CLOUD, 1)
        };

        uint[] kBaudrate =
        {
            1000000,//1000kbps
            800000,//800kbps
            500000,//500kbps
            250000,//250kbps
            125000,//125kbps
            100000,//100kbps
            50000,//50kbps
            20000,//20kbps
            10000,//10kbps
            5000 //5kbps
        };
        uint[] kUSBCANFDAbit =
        {
            1000000, // 1Mbps
            800000, // 800kbps
            500000, // 500kbps
            250000, // 250kbps
            125000, // 125kbps
            100000, // 100kbps
            50000, // 50kbps
            800000, // 800kbps
        };
        uint[] kUSBCANFDDbit =
        {
            5000000, // 5Mbps
            4000000, // 4Mbps
            2000000, // 2Mbps
            1000000, // 1Mbps
            800000, // 800kbps
            500000, // 500kbps
            250000, // 250kbps
            125000, // 125kbps
            100000, // 100kbps
        };
        uint[] kPCIECANFDAbit =
        {
            1000000, // 1Mbps
            800000, // 800kbps
            500000, // 500kbps
            250000, // 250kbps
        };
        uint[] kPCIECANFDDbit =
        {
            8000000, // 8Mbps
            4000000, // 4Mbps
            2000000, // 2Mbps
        };
        #endregion

        #region field
        IntPtr deviceHandle;
        IntPtr channelHandle;
        bool isDeveiceOpen = false;
        bool isEnableResistance = true;

        int channelIndex = 0;
        int canStandard = 0;

        byte mode = 0;

        Receive receive = null;

        object locker = new object();

        private int[] maskCode = new int[4] { 0xD1, 0xF7, 0x65, 0xDA };

        public delegate void RecvDataHandle(uint addr, byte[] data);
        public event RecvDataHandle RecvDataEvent;
        #endregion

        #region method
        private void OnRecvDataChanged(uint addr, byte[] data)
        {
            if(RecvDataEvent != null)
            {
                RecvDataEvent(addr, data);
            }
        }

        private bool OpenDevice()
        {
            deviceHandle = Method.ZCAN_OpenDevice(Define.ZCAN_USBCANFD_200U, 0, 0);
            isDeveiceOpen = (int)deviceHandle != NULL;
            return isDeveiceOpen;
        }

        private bool InitDevice()
        {
            if (isDeveiceOpen)
            {
                if (!SetCANStandard()) return false;
                if (!SetCANFDBaudrate()) return false;

                ZCAN_CHANNEL_INIT_CONFIG config = new ZCAN_CHANNEL_INIT_CONFIG();
                config.canfd.mode = mode;
                config.can_type = Define.TYPE_CANFD;

                IntPtr pConfig = Marshal.AllocHGlobal(Marshal.SizeOf(config));
                Marshal.StructureToPtr(config, pConfig, true);

                channelHandle = Method.ZCAN_InitCAN(deviceHandle, (uint)channelIndex, pConfig);
                Marshal.FreeHGlobal(pConfig);

                if (NULL == (int)channelHandle) return false;

                if (!SetResistanceEnable()) return false;

                if (!SetFilter()) return false;

            }
            return true && isDeveiceOpen;
        }


        public bool Start()
        {
            if (!OpenDevice()) return false;
            if (!InitDevice()) return false;
            if (Method.ZCAN_StartCAN(channelHandle) != Define.STATUS_OK) return false;
            if (null == receive)
            {
                receive = new Receive();
                receive.setChannelHandle(channelHandle);
                receive.setStart(true);
                receive.RecvFDData += this.AddData;
            }
            else
            {
                receive.setChannelHandle(channelHandle);
            }
            return true;
        }

        public void Stop()
        {
            Method.ZCAN_CloseDevice(deviceHandle);
        }

        public bool SendCANFDData(string msgId, string data)
        {
            uint id = (uint)Convert.ToInt32(msgId, 16);
            uint result; //发送的帧数
            ZCAN_TransmitFD_Data canfd_data = new ZCAN_TransmitFD_Data();
            canfd_data.frame.can_id = MakeCanId(id, 0, 0, 0);
            canfd_data.frame.data = new byte[64];
            canfd_data.frame.len = (byte)SplitData(data, ref canfd_data.frame.data, CANFD_MAX_DLEN);
            canfd_data.transmit_type = 0;
            canfd_data.frame.flags = 0;
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(canfd_data));
            Marshal.StructureToPtr(canfd_data, ptr, true);
            result = Method.ZCAN_TransmitFD(channelHandle, ptr, 1);
            Marshal.FreeHGlobal(ptr);
            return result == 1;
        }
        private int SplitData(string data, ref byte[] transData, int maxLen)
        {
            string[] dataArray = data.Split(' ');
            for (int i = 0; (i < maxLen) && (i < dataArray.Length); i++)
            {
                transData[i] = Convert.ToByte(dataArray[i], 16);
            }

            return dataArray.Length;
        }
        private bool SetCANStandard()
        {
            string path = channelIndex + "/canfd_standard";
            string value = canStandard.ToString();
            return 1 == Method.ZCAN_SetValue(deviceHandle, path, Encoding.ASCII.GetBytes(value));
        }

        private bool SetCANFDBaudrate()
        {
            //测试为ZCAN_USBCANFD_200U型号
            string path = "0/set_device_recv_merge";
            string value = "0";
            Method.ZCAN_SetValue(deviceHandle, path, Encoding.ASCII.GetBytes(value));
            string path1 = channelIndex + "/canfd_abit_baud_rate";
            string path2 = channelIndex + "/canfd_dbit_baud_rate";
            string value1 = 500000.ToString();
            string value2 = 2000000.ToString();
            uint result1 = Method.ZCAN_SetValue(deviceHandle, path1, Encoding.ASCII.GetBytes(value1));
            uint result2 = Method.ZCAN_SetValue(deviceHandle, path2, Encoding.ASCII.GetBytes(value2));
            return result1 == 1 && result2 == 1;
        }

        private bool SetResistanceEnable()
        {
            string path = channelIndex + "/initenal_resistance";
            string value = isEnableResistance ? "1" : "0";
            return 1 == Method.ZCAN_SetValue(deviceHandle, path, Encoding.ASCII.GetBytes(value));
        }

        private bool SetFilter()
        {
            //目前滤波模式为禁能，清除滤波
            string path = channelIndex + "/filter_clear";//清除滤波
            string value = "0";
            return 0 != Method.ZCAN_SetValue(deviceHandle, path, Encoding.ASCII.GetBytes(value));
        }

        private void AddData(ZCAN_ReceiveFD_Data[] data, uint len)
        {
            for (uint i = 0; i < len; ++i)
            {
                ZCAN_ReceiveFD_Data canfd = data[i];
                uint id = data[i].frame.can_id;                
                OnRecvDataChanged(GetId(id), canfd.frame.data);
            }
        }

        private uint MakeCanId(uint id, int eff, int rtr, int err)//1:extend frame 0:standard frame
        {
            uint ueff = (uint)(!!(Convert.ToBoolean(eff)) ? 1 : 0);
            uint urtr = (uint)(!!(Convert.ToBoolean(rtr)) ? 1 : 0);
            uint uerr = (uint)(!!(Convert.ToBoolean(err)) ? 1 : 0);
            return id | ueff << 31 | urtr << 30 | uerr << 29;
        }

        private bool IsEFF(uint id)//1:extend frame 0:standard frame
        {
            return !!Convert.ToBoolean((id & CAN_EFF_FLAG));
        }

        private bool IsRTR(uint id)//1:remote frame 0:data frame
        {
            return !!Convert.ToBoolean((id & CAN_RTR_FLAG));
        }

        private bool IsERR(uint id)//1:error frame 0:normal frame
        {
            return !!Convert.ToBoolean((id & CAN_ERR_FLAG));
        }

        private uint GetId(uint id)
        {
            return id & CAN_ID_FLAG;
        }

        public string GetKeys(string[] seed)
        {
            //Step1:与掩码进行异或运算
            string[] keys1 = new string[4];
            string result = string.Empty;
            for (int i = 0; i < maskCode.Length; i++)
            {
                int value = Convert.ToInt32(seed[i], 16) ^ maskCode[i];
                var hexstr = value.ToString("X");
                if (hexstr.Length < 2)
                {
                    hexstr = "0" + hexstr;
                }
                keys1[i] = hexstr;
            }

            //step2:将keys按照bit位进行翻转，获取新的key2
            string[] keys2 = new string[4];
            for (int i = 0; i < maskCode.Length; i++)
            {
                var bits = HexString2BinString(seed[i]).Trim();
                var reverse = bits.ToString().Reverse();
                string revStr = string.Empty;
                foreach (var charNum in reverse)
                {
                    revStr += charNum.ToString();
                }
                var hexStr = Convert.ToInt32(revStr, 2).ToString("X");
                keys2[maskCode.Length - i - 1] = hexStr;
            }

            //step3:将key2与掩码进行异或运算
            for (int i = 0; i < maskCode.Length; i++)
            {
                int value = Convert.ToInt32(keys2[i], 16) ^ maskCode[i];

                var hexstr = value.ToString("X");
                if (hexstr.Length < 2)
                {
                    hexstr = "0" + hexstr;
                }
                keys2[i] = hexstr;
            }

            int[] keys = new int[4];
            //Step4:将Key1和Key2相加并丢弃最终进位位（如果有的话）以获得密钥（key[4]）
            int flag = 0;
            for (int i = maskCode.Length - 1; i >= 0; i--)
            {
                keys[i] = Add(Convert.ToInt32(keys1[i], 16), Convert.ToInt32(keys2[i], 16), ref flag);
            }
            foreach (var item in keys)
            {
                result += item.ToString("X") + " ";
            }
            return result.Substring(0, result.Length - 1);
        }

        private int Add(int num1, int num2, ref int flag)
        {
            int value = num1 + num2 + flag;
            if ((value) >= 256)
            {
                flag = 1;
                value = value - 256;
            }
            else
            {
                flag = 0;
            }
            return value;
        }

        string HexString2BinString(string hexString)
        {
            string result = string.Empty;
            foreach (char c in hexString)
            {
                int v = Convert.ToInt32(c.ToString(), 16);
                int v2 = int.Parse(Convert.ToString(v, 2));
                // 去掉格式串中的空格，即可去掉每个4位二进制数之间的空格，
                result += string.Format("{0:d4}", v2);
            }
            return result;
        }
        #endregion
    }
}
